import inspect
import logging
import re
from abc import ABCMeta
from copy import deepcopy
from functools import wraps
from typing import Callable, Optional, Type, get_args, get_origin

try:
    from typing import Annotated
except ImportError:
    from typing_extensions import Annotated

from griffe import Docstring

try:
    from griffe import DocstringSectionKind
except ImportError:
    from griffe.enumerations import DocstringSectionKind

from ..schema import ActionReturn, ActionStatusCode
from .parser import BaseParser, JsonParser, ParseError

logging.getLogger('griffe').setLevel(logging.ERROR)


def tool_api(func: Optional[Callable] = None,
             *,
             explode_return: bool = False,
             returns_named_value: bool = False,
             **kwargs):
    '''一个装饰器函数，主要用于为动作(Action)类中的方法添加API描述信息。
    
    参数:
        func: 要装饰的函数或方法
        explode_return: 是否将返回值分解为多个值
        returns_named_value: 是否将返回值作为命名值返回
        **kwargs: 其他可选参数
    '''

    def _detect_type(string):
        """
        将Python类型字符串转换为标准化的类型表示
        
        @param {string} string - Python类型的字符串表示
        @return {string} 标准化后的类型字符串
        """
        # 默认将所有类型视为字符串类型
        field_type = 'STRING'  
        
        # 检测列表类型
        if 'list' in string:
            field_type = 'Array'
        # 如果不是字符串类型，则进一步判断其他类型
        elif 'str' not in string:
            # 检测浮点数类型
            if 'float' in string:
                field_type = 'FLOAT'
            # 检测整数类型
            elif 'int' in string:
                field_type = 'NUMBER'
            # 检测布尔类型
            elif 'bool' in string:
                field_type = 'BOOLEAN'
            
        return field_type

    def _explode(desc):
        """
        解析函数文档字符串中的参数信息
        
        @param {string} desc - 函数的文档字符串
        @return {Array<Object>} 解析后的参数信息列表
        """
        # 初始化存储参数信息的列表
        kvs = []
        
        # 重新格式化文档字符串
        # 1. 添加"Args:"标记
        # 2. 移除每行开头的特殊字符（空格、-、+、*、#、.）
        # 3. 保持缩进格式
        desc = '\nArgs:\n' + '\n'.join([
            '    ' + item.lstrip(' -+*#.')
            for item in desc.split('\n')[1:] if item.strip()
        ])
        
        # 使用Docstring解析器按Google风格解析文档
        docs = Docstring(desc).parse('google')
        
        # 如果没有解析到文档，返回空列表
        if not docs:
            return kvs
        
        # 检查第一个文档块是否为参数部分
        if docs[0].kind is DocstringSectionKind.parameters:
            # 遍历所有参数
            for d in docs[0].value:
                # 将参数信息转换为字典格式
                d = d.as_dict()
                
                # 处理类型注解
                if not d['annotation']:
                    # 如果没有类型注解，删除该字段
                    d.pop('annotation')
                else:
                    # 如果有类型注解，将其转换为标准类型
                    d['type'] = _detect_type(d.pop('annotation').lower())
                    
                # 将处理后的参数信息添加到列表
                kvs.append(d)
                
        return kvs

    def _parse_tool(function):
        docs = Docstring(
            re.sub(':(.+?):`(.+?)`', '\\2', function.__doc__ or '')).parse(
                'google', returns_named_value=returns_named_value, **kwargs)
        desc = dict(
            name=function.__name__,  # 函数名
            # 如果第一个文档块是文本类型,则作为函数描述
            description=docs[0].value if docs[0].kind is DocstringSectionKind.text else '',
            parameters=[],  # 参数列表
            required=[],  # 必需参数列表
        )

        # 初始化参数文档和返回值文档
        args_doc, returns_doc = {}, []
        
        # 遍历解析后的文档块
        for doc in docs:
            # 处理参数文档块
            if doc.kind is DocstringSectionKind.parameters:
                for d in doc.value:
                    d = d.as_dict()  # 转换为字典格式
                    # 处理参数类型注解
                    if d.get('annotation'):
                        d['type'] = _detect_type(d.pop('annotation').lower())
                    else:
                        d['type'] = ''
                    args_doc[d['name']] = d  # 保存参数信息

            # 处理返回值文档块 
            if doc.kind is DocstringSectionKind.returns:
                for d in doc.value:
                    d = d.as_dict()  # 转换为字典格式
                    if not d['name']: d.pop('name')  # 移除空名称
                    # 处理返回值类型注解
                    if not d['annotation']:
                        d.pop('annotation')
                    else:
                        d['type'] = _detect_type(d.pop('annotation').lower())
                    returns_doc.append(d)  # 添加到返回值文档列表

        # 获取函数的参数签名
        sig = inspect.signature(function)
        
        # 遍历函数的所有参数
        for name, param in sig.parameters.items():
            # 跳过self参数
            if name == 'self':
                continue
                
            # 构造参数描述字典
            parameter = dict(
                name=param.name,  # 参数名
                type='STRING',    # 默认类型为STRING
                # 从文档中获取参数描述,如果没有则为空字符串
                description=args_doc.get(param.name,
                                         {}).get('description', ''))
                                         
            # 获取参数的类型注解
            annotation = param.annotation
            
            # 如果没有类型注解
            if annotation is inspect.Signature.empty:
                # 从文档中获取类型,默认为STRING
                parameter['type'] = args_doc.get(param.name,
                                                 {}).get('type', 'STRING')
            else:
                # 处理Annotated类型注解
                if get_origin(annotation) is Annotated:
                    annotation, info = get_args(annotation)
                    if info:
                        parameter['description'] = info
                        
                # 处理嵌套的类型注解
                while get_origin(annotation):
                    annotation = get_args(annotation)
                    
                # 将类型注解转换为字符串并检测类型
                parameter['type'] = _detect_type(str(annotation))
                
            # 将参数信息添加到描述字典
            desc['parameters'].append(parameter)
            
            # 如果参数没有默认值,则添加到必需参数列表
            if param.default is inspect.Signature.empty:
                desc['required'].append(param.name)

        # 处理返回值信息
        return_data = []
        if explode_return:
            # 解析返回值描述
            return_data = _explode(returns_doc[0]['description'])
        elif returns_named_value:
            # 使用文档中的返回值信息
            return_data = returns_doc
            
        # 如果有返回值信息,添加到描述字典
        if return_data:
            desc['return_data'] = return_data
            
        return desc

    # 如果传入的是可调用对象(函数)
    if callable(func):
        # 检查是否为异步函数
        if inspect.iscoroutinefunction(func):
            # 如果是异步函数,创建异步包装器
            @wraps(func)  # 保留原函数的元数据
            async def wrapper(self, *args, **kwargs):
                # 异步调用原函数
                return await func(self, *args, **kwargs)
        else:
            # 如果是同步函数,创建同步包装器
            @wraps(func)  # 保留原函数的元数据
            def wrapper(self, *args, **kwargs):
                # 同步调用原函数
                return func(self, *args, **kwargs)

        # 为包装器添加API描述属性
        wrapper.api_description = _parse_tool(func)
        return wrapper

    # 如果传入的不是函数,则返回装饰器函数
    def decorate(func):
        # 检查被装饰的函数是否为异步函数
        if inspect.iscoroutinefunction(func):
            # 如果是异步函数,创建异步包装器
            @wraps(func)  # 保留原函数的元数据
            async def wrapper(self, *args, **kwargs):
                # 异步调用原函数
                return await func(self, *args, **kwargs)
        else:
            # 如果是同步函数,创建同步包装器
            @wraps(func)  # 保留原函数的元数据
            def wrapper(self, *args, **kwargs):
                # 同步调用原函数
                return func(self, *args, **kwargs)

        # 为包装器添加API描述属性
        wrapper.api_description = _parse_tool(func)
        return wrapper

    # 返回装饰器函数
    return decorate


class ToolMeta(ABCMeta):
    """
    @description 工具类的元类，用于自动处理工具类的API描述
    @extends ABCMeta - 继承自抽象基类的元类
    """

    def __new__(mcs, name, base, attrs):
        """
        @description 创建新的工具类
        @param {type} mcs - 元类本身
        @param {str} name - 要创建的类的名称
        @param {tuple} base - 基类元组
        @param {dict} attrs - 类属性字典
        @return {type} 返回创建的新类
        """
        # 初始化工具描述
        is_toolkit, tool_desc = True, dict(
            name=name,
            description=Docstring(attrs.get('__doc__', '')).parse('google')[0].value
        )

        # 遍历类的所有属性
        for key, value in attrs.items():
            # 检查是否为带有api_description的可调用对象
            if callable(value) and hasattr(value, 'api_description'):
                api_desc = getattr(value, 'api_description')
                
                if key == 'run':
                    # 如果是run方法，将其API描述作为主要工具描述
                    tool_desc['parameters'] = api_desc['parameters']
                    tool_desc['required'] = api_desc['required']
                    if api_desc['description']:
                        tool_desc['description'] = api_desc['description']
                    if api_desc.get('return_data'):
                        tool_desc['return_data'] = api_desc['return_data']
                    is_toolkit = False  # 标记为单一工具
                else:
                    # 如果是其他API方法，添加到api_list中
                    tool_desc.setdefault('api_list', []).append(api_desc)

        # 检查工具类型的合法性
        if not is_toolkit and 'api_list' in tool_desc:
            # 不允许同时实现run方法和其他工具API
            raise KeyError('`run` and other tool APIs can not be implemented '
                         'at the same time')

        # 处理默认的run方法
        if is_toolkit and 'api_list' not in tool_desc:
            is_toolkit = False
            if callable(attrs.get('run')):
                # 如果存在未装饰的run方法，为其添加装饰器
                run_api = tool_api(attrs['run'])
                api_desc = run_api.api_description
                tool_desc['parameters'] = api_desc['parameters']
                tool_desc['required'] = api_desc['required']
                if api_desc['description']:
                    tool_desc['description'] = api_desc['description']
                if api_desc.get('return_data'):
                    tool_desc['return_data'] = api_desc['return_data']
                attrs['run'] = run_api
            else:
                # 如果没有run方法，设置空的参数列表
                tool_desc['parameters'], tool_desc['required'] = [], []

        # 设置类属性
        attrs['_is_toolkit'] = is_toolkit  # 标记是否为工具集
        attrs['__tool_description__'] = tool_desc  # 保存工具描述

        # 调用父类的__new__方法创建类
        return super().__new__(mcs, name, base, attrs)


class BaseAction(metaclass=ToolMeta):
    """
    @description 所有动作(Action)的基类
    @param {Optional[dict]} description - 动作的描述信息，默认为None
    @param {Type[BaseParser]} parser - 用于处理动作输入输出的解析器类，默认为JsonParser
    """

    def __init__(
        self,
        description: Optional[dict] = None,
        parser: Type[BaseParser] = JsonParser,
    ):
        """
        @description 初始化动作实例
        @param {Optional[dict]} description - 动作描述，如果为None则使用类定义中的描述
        @param {Type[BaseParser]} parser - 解析器类，用于处理输入输出
        """
        self._description = deepcopy(description or self.__tool_description__)
        self._name = self._description['name']
        self._parser = parser(self)

    def __call__(self, inputs: str, name='run') -> ActionReturn:
        """
        @description 调用动作执行方法
        @param {str} inputs - 输入参数字符串
        @param {str} name - 要调用的API名称，默认为'run'
        @return {ActionReturn} 动作执行结果
        """
        # 保存原始输入参数，用于错误回退
        fallback_args = {'inputs': inputs, 'name': name}
        
        # 检查API是否存在
        if not hasattr(self, name):
            return ActionReturn(
                fallback_args,
                type=self.name,
                errmsg=f'invalid API: {name}',
                state=ActionStatusCode.API_ERROR)
        
        try:
            # 解析输入参数
            inputs = self._parser.parse_inputs(inputs, name)
        except ParseError as exc:
            # 处理参数解析错误
            return ActionReturn(
                fallback_args,
                type=self.name,
                errmsg=exc.err_msg,
                state=ActionStatusCode.ARGS_ERROR)
        
        try:
            # 执行API调用
            outputs = getattr(self, name)(**inputs)
        except Exception as exc:
            # 处理API执行错误
            return ActionReturn(
                inputs,
                type=self.name,
                errmsg=str(exc),
                state=ActionStatusCode.API_ERROR)
        
        # 处理返回结果
        if isinstance(outputs, ActionReturn):
            # 如果返回值已经是ActionReturn类型
            action_return = outputs
            if not action_return.args:
                action_return.args = inputs
            if not action_return.type:
                action_return.type = self.name
        else:
            # 解析普通返回值
            result = self._parser.parse_outputs(outputs)
            action_return = ActionReturn(inputs, type=self.name, result=result)
            
        return action_return

    @property
    def name(self):
        """
        @description 获取动作名称
        @return {str} 动作名称
        """
        return self._name

    @property
    def is_toolkit(self):
        """
        @description 判断是否为工具集
        @return {bool} 是否为工具集
        """
        return self._is_toolkit

    @property
    def description(self) -> dict:
        """
        @description 获取动作描述
        @return {dict} 动作的完整描述信息
        """
        return self._description

    def __repr__(self):
        """
        @description 返回动作的字符串表示
        @return {str} 动作描述的字符串形式
        """
        return f'{self.description}'

    __str__ = __repr__


class AsyncActionMixin:
    """
    @description 异步动作混入类
    @details 提供异步操作支持的混入类，用于扩展BaseAction类以支持异步调用
    """

    async def __call__(self, inputs: str, name='run') -> ActionReturn:
        """
        @description 异步调用动作执行方法
        @param {str} inputs - 输入参数字符串
        @param {str} name - 要调用的API名称，默认为'run'
        @return {ActionReturn} 动作执行结果
        """
        # 保存原始输入参数，用于错误回退
        fallback_args = {'inputs': inputs, 'name': name}

        # 检查API是否存在
        if not hasattr(self, name):
            return ActionReturn(
                fallback_args,
                type=self.name,
                errmsg=f'invalid API: {name}',
                state=ActionStatusCode.API_ERROR)

        try:
            # 解析输入参数
            inputs = self._parser.parse_inputs(inputs, name)
        except ParseError as exc:
            # 处理参数解析错误
            return ActionReturn(
                fallback_args,
                type=self.name,
                errmsg=exc.err_msg,
                state=ActionStatusCode.ARGS_ERROR)

        try:
            # 异步执行API调用
            outputs = await getattr(self, name)(**inputs)
        except Exception as exc:
            # 处理API执行错误
            return ActionReturn(
                inputs,
                type=self.name,
                errmsg=str(exc),
                state=ActionStatusCode.API_ERROR)

        # 处理返回结果
        if isinstance(outputs, ActionReturn):
            # 如果返回值已经是ActionReturn类型
            action_return = outputs
            if not action_return.args:
                action_return.args = inputs
            if not action_return.type:
                action_return.type = self.name
        else:
            # 解析普通返回值
            result = self._parser.parse_outputs(outputs)
            action_return = ActionReturn(inputs, type=self.name, result=result)

        return action_return

